#ifndef __IM_STRING_H__
#define __IM_STRING_H__

#include <im_base_types.h>
#include <im_object.h>
#include <im_object_array.h>

/**
 * @defgroup IMString
 * @ingroup PublicIME
 * @{
 */

/** @struct IMString
 *  @brief IMString class is used to manipulate a piece of ascii
 *         or UTF-8 encoded string.
 */

IM_BEGIN_DECLS

#define IM_STRING(p)                  (IM_TYPE_INSTANCE_CHECK_CAST((p),IM_TYPE_STRING,IMString))
#define IM_CONST_STRING(p)            (IM_TYPE_INSTANCE_CHECK_CAST_CONST((p),IM_TYPE_STRING,IMString))
#define IM_STRING_CLASS(c)            (IM_TYPE_CLASS_CHECK_CAST((c),IM_TYPE_STRING,IMStringClass))
#define IM_IS_STRING(p)               (IM_TYPE_INSTANCE_CHECK_TYPE((p),IM_TYPE_STRING))
#define IM_IS_STRING_CLASS(c)         (IM_TYPE_CLASS_CHECK_TYPE((c),IM_TYPE_STRING))
#define IM_STRING_GET_CLASS(p)        (IM_TYPE_INSTANCE_GET_CLASS_CAST((p),IM_TYPE_STRING,IMStringClass))

#define IM_STRING_NPOS                (IMSize)(-1)

typedef struct _IMString         IMString;
typedef struct _IMStringClass    IMStringClass;

/**
 * @brief Create a new IMString object with specified string
 *        as initial content.
 *
 * @param cstr an initial string value of the IMString object to be created.
 *
 * @return the newly created IMString object.
 */
IMString*       im_string_new                   (const IMChar       *cstr);

/**
 * @brief Create a new IMString object with specified string and length
 *        as initial content.
 *
 * @param cstr an initial string value of the IMString object to be created.
 * @param length specified lenght of the the IMString object to be created.
 *
 * @return the newly created IMString object.
 */
IMString*       im_string_new_length            (const IMChar       *cstr,
                                                 IMSize              length);

/**
 * @brief Create a new IMString object with specified static string
 *        as initial content.
 *
 * @param cstr an initial string value of the static IMString object to be created.
 *
 * @return the newly created IMString object.
 */
IMString*       im_string_new_static            (const IMChar       *cstr);

/**
 * @brief Create a new IMString object with enough memory for specified
 *        number of characters to prevent reallocation when inserting
 *        many characters.
 *
 * @param capacity specified max number of characters that the IMString object that to be created can hold.
 *
 * @return the newly created IMString object.
 */
IMString*       im_string_new_reserved          (IMSize              capacity);

/**
 * @brief Create a new IMString object from arbitrary printf result.
 *
 * @param format format string.
 * @param ... arbitrary variables to be printed according to format string.
 *
 * @return the newly created IMString object.
 */
IMString*       im_string_new_from_printf       (const IMChar       *format,
                                                 ...) IM_GNUC_PRINTF(1,2);

/**
 * @brief Create a new IMString object from a specified IMText object.
 *
 * The content of the specified IMText object will be converted into
 * UTF-8 encoded string and assigned to the newly created IMString
 * object.
 *
 * @param text Pointer to an IMText object.
 *
 * @return the newly created IMString object.
 */
IMString*       im_string_new_from_text_utf8    (IMConstPointer      text);

/**
 * @brief Erases the content of a IMString object, making it empty.
 *
 * All content will be removed.
 *
 * @param string the IMString object it's conent will be removed.
 *
 * @return void.
 */
void            im_string_clear                 (IMString           *string);

/**
 * @brief Preallocate enough memory for specified number of characters
 *        to prevent reallocation when inserting many characters.
 *
 * @param string an IMString object.
 * @param size number of characters required.
 *
 * @return void.
 */
void            im_string_reserve               (IMString           *string,
                                                 IMSize              size);
/**
 * @brief get the total number of characters that
 *        the IMString can hold before needing to allocate
 *        more memory.
 *
 * @param string an IMString object.
 *
 * @return the total number of characters that
 *        the IMString can hold before needing to allocate
 *        more memory.
 */
IMSize          im_string_capacity              (const IMString     *string);
/**
 * @brief Indentify whether an IMString object is empty or not.
 *
 * @param string an IMString object.
 *
 * @return true if the IMString object is empty, false if the IMString object is not empty.
 */
IMBool          im_string_empty                 (const IMString     *string);

/**
 * @brief Resizes the IMString object to the specified number of characters.
 *
 * This function will resize the IMString object to the specified number
 * of characters. If the number is smaller than the IMString's current size
 * the IMString is truncated, otherwise the IMString is extended and new
 * elements are set to fill_ch.
 *
 * @param string IMString object to be resized.
 * @param new_size number of characters the IMString should contain.
 * @param fill_ch character to fill any new elements.
 *
 * @return void.
 */
void            im_string_resize                (IMString           *string,
                                                 IMSize              new_size,
                                                 IMChar              fill_ch);
/**
 * @brief Get the length of an IMString object.
 *
 * @param string an IMString object.
 *
 * @return length of an IMString object, in number of characters.
 */
IMSize          im_string_length                (const IMString     *string);

/**
 * @brief Hash function of IMString. Used with IMHashTable.
 *
 * @param string an IMString object.
 */
IMUInt          im_string_hash                  (const IMString     *string);

/**
 * @brief get the charactor list in an IMString object.
 *
 * @param string an IMString object.
 *
 * return the real content of an IMString object.
 */
const IMChar*   im_string_c_str                 (const IMString     *string);

/**
 * @brief Copy the content of specified C string to an IMString object.
 *
 * Old content of the IMString object will be replaced with the new string.
 *
 * @param string an IMString object
 * @param cstr a C string.
 * @param len length of the C string, -1 means until end of the string.
 *
 * @return Pointer pointed to the original string object.
 */
IMString*       im_string_assign                (IMString           *string,
                                                 const IMChar       *cstr,
                                                 IMSize              len);

/**
 * @brief Set a static C string as the content of specified IMString object.
 *
 * Old content of the IMString object will be replaced with the new string.
 *
 * @param string an IMString object
 * @param cstr a static C string.
 *
 * @return Pointer pointed to the original string object.
 */
IMString*       im_string_assign_static         (IMString           *string,
                                                 const IMChar       *cstr);

/**
 * @brief Copy the content of specified IMText object to an IMString object.
 *
 * The content of the specified IMText object will be converted into
 * UTF-8 encoded string and assigned to specified IMString object.
 *
 * @param string an IMString object.
 *
 * @param text Pointer to an IMText object.
 * 
 * @return an IMString object with UTF-8 encoded string. 
 */
IMString*       im_string_assign_text_utf8      (IMString           *string,
                                                 IMConstPointer      text);

/**
 * @brief Get the character at specified index in an IMString object.
 *
 * @param string an IMString object
 * @param idx the character index, must less than string length.
 *
 * @return the unicode character at the index,
 *         or 0 if the index is invalid.
 */
IMChar          im_string_get_char              (const IMString     *string,
                                                 IMSize              idx);

/**
 * @brief Set the character at specified index in an IMString object to
 *        a given character.
 *
 * @param string an IMString object
 * @param idx the character index,
 *        must less than the length of this IMString object.
 * @param ch The character code to be set.
 *
 * @return void.
 */
void            im_string_set_char              (IMString           *string,
                                                 IMSize              idx,
                                                 IMChar              ch);
/**
 * @brief Create a new IMString object containing a sub portion of a given
 *        IMString object.
 *
 * @param string an IMString object.
 * @param pos start position of the sub portion.
 * @param len length of the sub portion.
 *
 * @return a newly created IMString object containing the sub string. It must
 *         be released by im_object_unref() afterwards.
 */
IMString*       im_string_get_sub_string        (const IMString     *string,
                                                 IMSize              pos,
                                                 IMSize              len);
/**
 * @brief Prepend an IMString object into the beginning of a IMString object.
 *
 * @param string an IMString object.
 * @param other an IMString object which will be prepended into string.
 *
 * @return string.
 */
IMString*       im_string_prepend               (IMString           *string,
                                                 const IMString     *other);

/**
 * @brief Prepend a C string into the beginning of a IMString object.
 *
 * @param string an IMString object.
 * @param other a string which will be prepended into string.
 * @param len length of other, -1 means until end of the string.
 *
 * @return an IMString object.
 */
IMString*       im_string_prepend_str           (IMString           *string,
                                                 const IMChar       *other,
                                                 IMSize              len);

/**
 * @brief Prepend an unicode character into the beginning of a IMString object.
 *
 * @param string an IMString object.
 * @param ch a character which will be prepended into string.
 *
 * @return an IMString object.
 */
IMString*       im_string_prepend_char          (IMString           *string,
                                                 IMChar              ch);
/**
 * @brief Append an IMString object into the end of a IMString object.
 *
 * @param string an IMString object.
 * @param other an IMString object which will be appended into string.
 *
 * @return an IMString object.
 */
IMString*       im_string_append                (IMString           *string,
                                                 const IMString     *other);

/**
 * @brief Append a C string into the beginning of a IMString object.
 *
 * @param string an IMString object.
 * @param other a string which will be appended into string.
 * @param len length of other, -1 means until end of the string.
 *
 * @return an IMString object.
 */
IMString*       im_string_append_str            (IMString           *string,
                                                 const IMChar       *other,
                                                 IMSize              len);

/**
 * @brief Append an unicode character into the end of a IMString object.
 *
 * @param string an IMString object.
 * @param ch a character which will be appended into string.
 *
 * @return an IMString object.
 */
IMString*       im_string_append_char           (IMString           *string,
                                                 IMChar              ch);
/**
 * @brief Insert an IMString object into an IMString object at the specified position.
 *
 * @param string an IMString object.
 * @param pos position in string.
 * @param other an IMString object to be inserted into string.
 * 
 * @return an IMString object.
 */
IMString*       im_string_insert                (IMString           *string,
                                                 IMSize              pos,
                                                 const IMString     *other);

/**
 * @brief Insert a C string into the beginning of a IMString object.
 *
 * @param string an IMString object.
 * @param pos position in string.
 * @param other a string which will be inserted into string.
 * @param len length of other, -1 means until end of the string.
 * 
 * @return an IMString object.
 */
IMString*       im_string_insert_str            (IMString           *string,
                                                 IMSize              pos,
                                                 const IMChar       *other,
                                                 IMSize              len);

/**
 * @brief Insert an unicode character into an IMString object at the specified position.
 *
 * @param string an IMString object.
 * @param pos position in string.
 * @param ch an character to be inserted into string.
 *
 * @return an IMString object.
 */
IMString*       im_string_insert_char           (IMString           *string,
                                                 IMSize              pos,
                                                 IMChar              ch);
/**
 * @brief Erase a specified portion of an IMString object.
 *
 * @param string an IMString object.
 * @param pos position of the portion to be erased.
 * @param len length of the portion.
 *
 * @return an IMString object.
 */
IMString*       im_string_erase                 (IMString           *string,
                                                 IMSize              pos,
                                                 IMSize              len);
/**
 * @brief Compare two IMString objects.
 * 
 * @string1 the first IMString object to be compared.
 * @string2 the second IMString object to be compared.
 *
 * @return -1 means string1 < string2, 0 means they are equal,
 *         1 means string1 > string2.
 */
IMInt           im_string_compare               (const IMString     *string1,
                                                 const IMString     *string2);
/**
 * @brief Compare an IMString object with a C string.
 *
 * @string1 the IMString object to be compared.
 * @string2 the C String to be compared.
 
 * @return -1 means string1 < string2, 0 means they are equal,
 *         1 means string1 > string2.
 */
IMInt           im_string_compare_str           (const IMString     *string1,
                                                 const IMChar       *string2);
/**
 * @brief Compare two IMString objects for a specified length.
 *
 * @string1 the first IMString object to be compared.
 * @string2 the second IMString object to be compared.
 *
 * Only a specified length of string content will be compared.
 */
IMInt           im_string_n_compare             (const IMString     *string1,
                                                 const IMString     *string2,
                                                 IMSize              len);
/**
 * @brief Compare an IMString object with a C string for a specified length.
 *
 * @string1 the first IMString object to be compared.
 * @string2 the second IMString object to be compared.
 *
 * Only a specified length of string content will be compared.
 */
IMInt           im_string_n_compare_str         (const IMString     *string1,
                                                 const IMChar       *string2,
                                                 IMSize              len);
/**
 * @brief Compare two IMString objects ignoring the case of ASCII characters.
 *
 * @string1 the first IMString object to be compared.
 * @string2 the second IMString object to be compared.
 *
 * @return -1 means string1 < string2, 0 means they are equal,
 *         1 means string1 > string2.
 */
IMInt           im_string_ascii_case_compare    (const IMString     *string1,
                                                 const IMString     *string2);
/**
 * @brief Compare an IMString object with a C string ignoring the case of
 * ASCII characters.
 *
 * @string1 the first IMString object to be compared.
 * @string2 the second IMString object to be compared.
 *
 * @return -1 means string1 < string2, 0 means they are equal,
 *         1 means string1 > string2.
 */
IMInt           im_string_ascii_case_compare_str(const IMString     *string1,
                                                 const IMChar       *string2);
/**
 * @brief Compare two IMString objects for a specified length
 *        ignoring the case of ASCII characters.
 * Only a specified length of string content will be compared.
 *
 * @string1 the first IMString object to be compared.
 * @string2 the second IMString object to be compared.
 *
 * @return -1 means string1 < string2, 0 means they are equal,
 *         1 means string1 > string2.
 */
IMInt           im_string_ascii_n_case_compare  (const IMString     *string1,
                                                 const IMString     *string2,
                                                 IMSize              len);
/**
 * @brief Compare an IMString object with a C string for a specified length
 *        ignoring the case of ASCII characters.
 * Only a specified length of string content will be compared.
 *
 * @string1 the first IMString object to be compared.
 * @string2 the second IMString object to be compared.
 *
 * @return -1 means string1 < string2, 0 means they are equal,
 *         1 means string1 > string2.
 */
IMInt           im_string_ascii_n_case_compare_str(const IMString     *string1,
                                                   const IMChar       *string2,
                                                   IMSize              len);
/**
 * @brief Prepend arbitrary UTF-8 encoded printf result
 *        to a specified string.
 *
 * @param string a IMString object.
 * @param format format string.
 * @param ... arbitrary variables to be printed according to format string.
 *
 * @return an IMString object.
 */
IMString*       im_string_prepend_printf        (IMString           *string,
                                                 const IMChar       *format,
                                                 ...) IM_GNUC_PRINTF(2,3);

/**
 * @brief Append arbitrary UTF-8 encoded printf result
 *        to a specified string.
 *
 * @param string a IMString object.
 * @param format format string.
 * @param ... arbitrary variables to be printed according to format string.
 *
 * @return an IMString object.
 */
IMString*       im_string_append_printf         (IMString           *string,
                                                 const IMChar       *format,
                                                 ...) IM_GNUC_PRINTF(2,3);

/**
 * @brief Insert arbitrary UTF-8 encoded printf result
 *        to a specified string at specified position.
 *
 * @param string a IMString object.
 * @param format format string.
 * @param ... arbitrary variables to be printed according to format string.
 *
 * @return an IMString object.
 */
IMString*       im_string_insert_printf         (IMString           *string,
                                                 IMSize              pos,
                                                 const IMChar       *format,
                                                 ...) IM_GNUC_PRINTF(3,4);

/**
 * @brief Print arbitrary string into an IMString object.
 *
 * Old content of target IMString object will be erased.
 *
 * @param string a IMString object.
 * @param format format string.
 * @param ... arbitrary variables to be printed according to format string.
 *
 * @return an IMString object.
 */
IMString*       im_string_printf                (IMString           *string,
                                                 const IMChar       *format,
                                                 ...) IM_GNUC_PRINTF(2,3);

/**
 * @brief Find position of a string.
 *
 * Starting from pos, searches forward for value of other within
 * this string.  If found, returns the index where it begins.
 * If not found, returns IM_STRING_NPOS.
 *
 * @param string an IMString object.
 * @param other  an IMString object to locate inside string.
 * @param pos Index of character to search from
 *
 * @return Index of start of first occurrence,
 *         or IM_STRING_NPOS if not found.
 */
IMSize          im_string_find                  (const IMString     *string,
                                                 const IMString     *other,
                                                 IMSize              pos);

/**
 * @brief Find position of a string.
 *
 * Starting from pos, searches forward for other within
 * this string.  If found, returns the index where it begins.
 * If not found, returns IM_STRING_NPOS.
 *
 * @param string an IMString object.
 * @param other  a C string to locate inside string.
 * @param pos Index of character to search from
 * @param len Number of characters from other to search for.
 *
 * @return Index of start of first occurrence,
 *         or IM_STRING_NPOS if not found.
 */
IMSize          im_string_find_str              (const IMString     *string,
                                                 const IMChar       *other,
                                                 IMSize              pos,
                                                 IMSize              len);

/**
 * @brief Find position of a string.
 *
 * Starting from pos, searches forward for specified character
 * within this string.  If found, returns the index where it begins.
 * If not found, returns IM_STRING_NPOS.
 *
 * @param string an IMString object.
 * @param ch a character to locate inside string.
 * @param pos Index of character to search from
 *
 * @return Index of start of first occurrence,
 *         or IM_STRING_NPOS if not found.
 */
IMSize          im_string_find_char             (const IMString     *string,
                                                 IMChar              ch,
                                                 IMSize              pos);

/**
 * @brief Find last position of a string.
 *
 * Starting from pos, searches backward for value of other within
 * this string.  If found, returns the index where it begins.
 * If not found, returns IM_STRING_NPOS.
 *
 * @param string an IMString object.
 * @param other  an IMString object to locate inside string.
 * @param pos Index of character to search from,
 *            IM_STRING_NPOS means starting from end of string.
 *
 * @return Index of start of first occurrence,
 *         or IM_STRING_NPOS if not found.
 */
IMSize          im_string_rfind                 (const IMString     *string,
                                                 const IMString     *other,
                                                 IMSize              pos);

/**
 * @brief Find last position of a string.
 *
 * Starting from pos, searches backward for other within
 * this string.  If found, returns the index where it begins.
 * If not found, returns IM_STRING_NPOS.
 *
 * @param string an IMString object.
 * @param other  a C string to locate inside string.
 * @param pos Index of character to search from,
 *            IM_STRING_NPOS means starting from end of string.
 * @param len Number of characters from other to search for.
 *
 * @return Index of start of first occurrence,
 *         or IM_STRING_NPOS if not found.
 */
IMSize          im_string_rfind_str             (const IMString     *string,
                                                 const IMChar       *other,
                                                 IMSize              pos,
                                                 IMSize              len);

/**
 * @brief Find last position of a string.
 *
 * Starting from pos, searches backward for specified character
 * within this string.  If found, returns the index where it begins.
 * If not found, returns IM_STRING_NPOS.
 *
 * @param string an IMString object.
 * @param ch a character to locate inside string.
 * @param pos Index of character to search from,
 *        IM_STRING_NPOS means starting from end of string.
 *
 * @return Index of start of first occurrence,
 *         or IM_STRING_NPOS if not found.
 */
IMSize          im_string_rfind_char            (const IMString     *string,
                                                 IMChar              ch,
                                                 IMSize              pos);

/**
 * @brief  Find position of a character of string.
 *
 * Starting from pos, searches forward for one of the characters of
 * other within this string.  If found, returns the index where it was
 * found.  If not found, returns IM_STRING_NPOS.
 *
 * @param string an IMString object.
 * @param other String containing characters to locate.
 * @param pos  Index of character to search from.
 *
 * @return  Index of first occurrence,
 *          or IM_STRING_NPOS if not found.
 *
 */
IMSize          im_string_find_first_of         (const IMString     *string,
                                                 const IMString     *other,
                                                 IMSize              pos);

/**
 * @brief  Find position of a character of string.
 *
 * Starting from pos, searches forward for one of the characters of
 * other within this string.  If found, returns the index where it was
 * found.  If not found, returns IM_STRING_NPOS.
 *
 * @param string an IMString object.
 * @param other String containing characters to locate.
 * @param pos  Index of character to search from.
 * @param len  Number of characters from other to search for
 *
 * @return  Index of first occurrence,
 *          or IM_STRING_NPOS if not found.
 *
 */
IMSize          im_string_find_first_of_str     (const IMString     *string,
                                                 const IMChar       *other,
                                                 IMSize              pos,
                                                 IMSize              len);

/**
 * @brief  Find last position of a character of string.
 *
 * Starting from pos, searches backward for one of the characters of
 * other within this string.  If found, returns the index where it was
 * found.  If not found, returns IM_STRING_NPOS.
 *
 * @param string an IMString object.
 * @param other String containing characters to locate.
 * @param pos  Index of character to search from,
 *             IM_STRING_NPOS means starting from end of string.
 *
 * @return  Index of first occurrence,
 *          or IM_STRING_NPOS if not found.
 *
 */
IMSize          im_string_find_last_of          (const IMString     *string,
                                                 const IMString     *other,
                                                 IMSize              pos);

/**
 * @brief  Find last position of a character of string.
 *
 * Starting from pos, searches backward for one of the characters of
 * other within this string.  If found, returns the index where it was
 * found.  If not found, returns IM_STRING_NPOS.
 *
 * @param string an IMString object.
 * @param other String containing characters to locate.
 * @param pos  Index of character to search from.
 *             IM_STRING_NPOS means starting from end of string.
 * @param len  Number of characters from other to search for
 *
 * @return  Index of first occurrence,
 *          or IM_STRING_NPOS if not found.
 */
IMSize          im_string_find_last_of_str      (const IMString     *string,
                                                 const IMChar       *other,
                                                 IMSize              pos,
                                                 IMSize              len);

/**
 * @brief Convert all alphabet characters in a specified string to lowercase.
 * 
 * @param string an IMString object to converted.
 *
 * @return void.
 */
void            im_string_ascii_tolower         (IMString           *string);

/**
 * @brief Convert all alphabet characters in a specified string to uppercase.
 * 
 * @param string an IMString object to converted.
 *
 * @return void.
 */
void            im_string_ascii_toupper         (IMString           *string);

/**
 * @brief Convert all alphabet characters in a specified range
 *        of a string to lowercase.
 * 
 * @param string an IMString object to converted.
 * @param pos the start position of the string.
 * @param len the number of charator to converted for the start position.
 *
 * @return void.
 */
void            im_string_ascii_tolower_range   (IMString           *string,
                                                 IMSize              pos,
                                                 IMSize              len);

/**
 * @brief Convert all alphabet characters in a specified range
 *        of a string to uppercase.
 *
 * @param string an IMString object to converted.
 * @param pos the start position of the string.
 * @param len the number of charator to converted for the start position.
 *
 * @return void.
 */
void            im_string_ascii_toupper_range   (IMString           *string,
                                                 IMSize              pos,
                                                 IMSize              len);

/**
 * @brief Remove blank characters at the begin and end of a string.
 *
 * @param string an IMString object to converted.
 *
 * @return void.
 */
void            im_string_trim_blanks           (IMString           *string);

/**
 * @brief Split the specified string into sub strings by one of given delimiter
 * chars.
 *
 * @param string an IMString object to be splitted.
 * @param delim_chars given delimiter chars
 *
 * @return An object array which holdes all sub strings. It must be released
 *  by caller.
 */
IMObjectArray*  im_string_split                 (const IMString     *string,
                                                 const IMChar       *delim_chars);
/**
 * @brief Get the left portion of a specified string, which is splitted by one
 * of given delimiters.
 * If there are more than one delimiters, then the string will be splitted by
 * the left most delimiter.
 * White space around the sub string will be trimmed.
 *
 * For example, im_string_get_left_portion("abc = foo", "=") will return "abc".
 *
 * The returned string should be freed afterwards by calling im_object_unref();
 *
 * @param string an IMString object to be splitted.
 * @param delim_chars given delimiter chars
 *
 * @return an new IMString object.
 */
IMString*       im_string_get_left_portion      (const IMString     *string,
                                                 const IMChar       *delim_chars);

/**
 * @brief Get the right portion of a specified string, which is splitted by one
 * of given delimiters.
 * If there are more than one delimiters, then the string will be splitted by
 * the left most delimiter.
 * White space around the sub string will be trimmed.
 *
 * For example, im_string_get_left_portion("abc = foo", "=") will return "foo".
 *
 * The returned string should be freed afterwards by calling im_object_unref();
 *
 * @param string an IMString object to be splitted.
 * @param delim_chars given delimiter chars
 *
 * @return an new IMString object.
 */
IMString*       im_string_get_right_portion     (const IMString     *string,
                                                 const IMChar       *delim_chars);
IM_END_DECLS
/** @} */

#endif
/*
vi:ts=4:nowrap:ai:expandtab
*/
